﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyWeb.ServiceBus.Common
{
    public static class Snowflake
    {
        // 时间起始标记点，作为基准，一般取系统的最近时间（一旦确定不能变动）
        private static readonly long Twepoch = 1288834974657L; // 2010-11-04 09:42:54

        // 机器标识位数
        private const int WorkerIdBits = (int)5L;
        // 数据中心标识位数
        private const int DatacenterIdBits = (int)5L;
        // 序列号位数
        private const int SequenceBits = (int)12L;

        // 支持的最大机器ID
        private const long MaxWorkerId = -1L ^ (-1L << WorkerIdBits);
        // 支持的最大数据中心ID
        private const long MaxDatacenterId = -1L ^ (-1L << DatacenterIdBits);

        // 机器ID偏移量
        private const int WorkerIdShift = SequenceBits;
        // 数据中心ID偏移量
        private const int DatacenterIdShift = SequenceBits + WorkerIdBits;
        // 时间戳偏移量
        private const int TimestampLeftShift = SequenceBits + WorkerIdBits + DatacenterIdBits;

        // 序列号掩码
        private const long SequenceMask = -1L ^ (-1L << SequenceBits);

        // 工作机器ID
        private static readonly long workerId = 1;
        // 数据中心ID
        private static readonly long datacenterId = 1;
        // 序列号
        private static long sequence = 0L;
        // 上次时间戳
        private static long lastTimestamp = -1L;

        //public Snowflake(long workerId, long datacenterId)
        //{
        //    if (workerId > MaxWorkerId || workerId < 0)
        //    {
        //        throw new ArgumentException($"worker Id can't be greater than {MaxWorkerId} or less than 0");
        //    }
        //    if (datacenterId > MaxDatacenterId || datacenterId < 0)
        //    {
        //        throw new ArgumentException($"datacenter Id can't be greater than {MaxDatacenterId} or less than 0");
        //    }
        //    this.workerId = workerId;
        //    this.datacenterId = datacenterId;
        //}
        private static readonly object _lock = new();
        public static long NextId()
        {
            lock (_lock)
            {
                var timestamp = TimeGen();

                if (timestamp < lastTimestamp)
                {
                    throw new InvalidOperationException("Clock moved backwards. Refusing to generate id for " + (lastTimestamp - timestamp) + " milliseconds");
                }

                if (lastTimestamp == timestamp)
                {
                    // 同一毫秒内，序列号自增
                    sequence = (sequence + 1) & SequenceMask;
                    if (sequence == 0)
                    {
                        // 同一毫秒的序列数已经达到最大值，等待下一毫秒
                        timestamp = TilNextMillis(lastTimestamp);
                    }
                }
                else
                {
                    // 不同毫秒内，序列号置为0
                    sequence = 0L;
                }

                lastTimestamp = timestamp;

                // 生成ID
                return ((timestamp - Twepoch) << TimestampLeftShift) |
                       (datacenterId << DatacenterIdShift) |
                       (workerId << WorkerIdShift) |
                       sequence;
            }
        }

        private static long TilNextMillis(long lastTimestamp)
        {
            var timestamp = TimeGen();
            while (timestamp <= lastTimestamp)
            {
                timestamp = TimeGen();
            }
            return timestamp;
        }

        private static long TimeGen()
        {
            return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
        }
    }
}
